using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using NBusiness.Utilities;
using NBusiness.CodeDom;
using NBusiness.CodeDom.Compiler;
using System.Reflection;
using System.Collections.Specialized;
using System.Runtime.InteropServices;
using NBusiness.CodeDom.Services;

namespace NBusiness.ESharp.Compiler
{
    [Serializable]
	internal sealed class ESharpParser : EntityParser
	{
		#region Member fields and propertise
        [NonSerialized]
        ESharpProvider _provider;

        #endregion

        #region Constructors

        public ESharpParser(ESharpProvider provider)
        {
            _provider = provider;
        } 

        #endregion

		#region Expressions
		public static class Expressions
		{
            // TODO: change documentation to reflect syntax changes
            // Syntax changes so far:
            // DateTime -> datetime
            // create -> insert
            // retrieve -> fetch
            // action types to lowercase
            // action in to on change
            // validation in addition

            //Parsing Elements
            public static Regex Element = new Regex("([/][/].*$)|(\"[^\"\r\n]*\")|(\\s)|([.][0-9]+)|([0-9]+[.][0-9]+)|([a-zA-Z0-9_]+)|(\\d+)|([{}[\\]();:=,*?.+\\-&<>!|\\/%$])", RegexOptions.Multiline);

            // Token Types
            public static Regex Keywords = new Regex("^(with|on|as|id|auto|nullable|readonly|in|when|async|family|entity|validate|access|field|validation|action|relationship|authorize|using)$");
            public static Regex Quoted = new Regex("^(\"[^\"\r\n]*\")$");
            public static Regex Literal = new Regex("^(['].[']|[0-9]+)$");
            public static Regex Delimiter = new Regex("$[.,]^");
            public static Regex WhiteSpace = new Regex(@"^\s+$");
            public static Regex LineComments = new Regex(@"^(//.*)$");
            public static Regex Identifier = new Regex(@"^[a-zA-Z]+[a-z0-9A-Z]*$"); 

			//CodeElements
			public static Regex RelationshipDeclaration = new Regex(@"^relationship$");
			public static Regex RelationshipWith = new Regex(@"^with$");
			public static Regex RelationshipOn = new Regex(@"^on$");
			public static Regex RelationshipSplit = new Regex(@",");
			public static Regex RelationshipAs = new Regex(@"^as$");
			public static Regex RelationshipType = new Regex(@"^(parent|child|sibling)$");
            public static Regex RelationshipAssign = new Regex(@"[=]");

			public static Regex FieldIdProperty = new Regex(@"^id$");
			public static Regex FieldAutoProperty = new Regex(@"^auto$");
            public static Regex FieldNullableProperty = new Regex(@"^nullable$");
            public static Regex FieldReadOnly = new Regex(@"^readonly$");
            public static Regex FieldTypes = new Regex("^(int|string|unique|bytes|short|long|decimal|double|float|datetime|bool)$");
            public static Regex FieldDeclaration = new Regex(@"^field$");

			public static Regex FamilyDeclaration = new Regex(@"^family$");
            public static Regex FamilyJoin = new Regex(@"[.]");

			public static Regex EntityDeclaration = new Regex(@"^entity$");
            public static Regex EntityAs = new Regex(@"^as$");
            public static Regex EntityTemplateSeparator = new Regex(@"[,]");
            public static Regex EntityTemplateJoin = new Regex(@"[.]");

            public static Regex ModelDeclaration = new Regex(@"^model$");
            public static Regex ModelReadOnly = new Regex(@"^readonly$");
            public static Regex ModelInherit = new Regex(@"^inherit$");
            public static Regex ModelInheritType = new Regex(@"^(field|relationship|validate|access|authorize|action)$");
            public static Regex ModelInheritTypeNameJoin = new Regex(@"[.]");

            //using declaration
            public static Regex UsingDeclaration = new Regex(@"^using$");
            public static Regex UsingJoin = new Regex(@"[.]");

            //action
            public static Regex ActionDeclaration = new Regex(@"^action$");
            public static Regex ActionOn = new Regex(@"^on$");
            public static Regex ActionTypeJoin = new Regex(@"[.]");
            public static Regex ActionWhen = new Regex(@"^when$");
            public static Regex ActionAsync = new Regex(@"^async$");
            public static Regex ActionWhenType = new Regex("(loading|loaded|fetching|fetched|persisting|persisted|inserting|inserted|updating|updated|deleting|deleted|validating|validated|authorizing|authorized|accessing|accessed|changed|error)");

            //rules
            public static Regex AccessDeclaration = new Regex(@"^access$");
            public static Regex AccessMethod = new Regex(@"^(get|set)$");

            public static Regex AuthorizeDeclaration = new Regex(@"^authorize$");
            public static Regex AuthorizeMethod = new Regex(@"^(insert|fetch|update|delete)$");

			public static Regex ValidateDeclaration = new Regex(@"^validate$");
            public static Regex ValidateArgSplit = new Regex(@"[,]");
            public static Regex ValidateArg = new Regex("^(([\"].*[\"])|(['].['])|([{][a-zA-Z0-9-]+[}])|(\\d+)|([.][0-9]+)|([0-9]+[.][0-9]+))$");
            public static Regex ValidateArgAssign = new Regex("[:]");
            public static Regex ValidateArgsBegin = new Regex("[{]");
            public static Regex ValidateArgsEnd = new Regex("[}]");

            //misc
			public static Regex DenyAllow = new Regex(@"^(deny|allow)$");
            public static Regex RoleName = new Regex("^([\"][a-zA-Z](\\w|[ ])*[\"])|([a-zA-Z](\\w)*)|[*]|[?]");
            public static Regex GroupBegin = new Regex(@"{");
            public static Regex GroupEnd = new Regex(@"}");
            public static Regex StatementEnd = new Regex(@";");
            public static Regex Command = new Regex(@"[{}[\]();:=,*?.+\-&<>!|\\/%$]");
            public static Regex Inherits = new Regex(@":");
		} 
		#endregion

        #region Parse

        public override EntityRoot ParseFiles(params EntityFile[] entityFiles)
        {
            ESharpRoot root = new ESharpRoot(_provider);
            foreach (EntityFile file in entityFiles)
            {
                EntityElement[] elements = GetElements(file);

                BuildRoot(root, elements);
            }
            (root as ICompileUnit).Attach();

            return root;
        }

        public override EntityRoot BuildRootFromElements(params EntityElement[] elements)
        {
            ESharpRoot root = new ESharpRoot(_provider);
            BuildRoot(root, elements);
            
            return root;
        }

        public override EntityRoot BuildRootFromElementsAndAttach(params EntityElement[] elements)
        {
            ESharpRoot root = BuildRootFromElements(elements) as ESharpRoot;
            (root as ICompileUnit).Attach();

            return root;
        }

        public override EntityElement[] GetElements(EntityFile file)
        {
            List<EntityElement> elements = new List<EntityElement>();

            int index = 0;
            int len = 0;
            string code = file.Data;
            MatchCollection matches = Expressions.Element.Matches(code);

            foreach(Match match in matches)
            {
                if (match.Index == index)
                    len = match.Value.Length;
                else
                    len = match.Index - index;

                string val = code.Substring(index, len);
                int line = ESharpRoot.GetLine(code, index);
                int col = ESharpRoot.GetColumn(code, index);
                EntityElement element = new EntityElement(val, line, col, index, index + len-1);
                element.Path = file.FileName;
                index += len;

                elements.Add(element);
            }

            return elements.ToArray();
        }

        #endregion

        #region Build root
        
        private void BuildRoot(ESharpRoot root, EntityElement[] elements)
        {
            Stack<EntityElement> stack = new Stack<EntityElement>();
            for (int x = elements.Length - 1; x >= 0; x--)
            {
                stack.Push(elements[x]);
            }

            (root as ICompileUnit).Build(stack, EntityElementType.Unknown);

            IBuildErrorService errorService = _provider.GetService(typeof(IBuildErrorService)) as IBuildErrorService;
            if (errorService != null)
            {
                var unknownQuery = from e in elements where e.Type == EntityElementType.Unknown select e;
                if (unknownQuery.Count() > 0 && _provider != null)
                {
                    errorService.Add(new ESharpBuildError(
                        ESharpErrorCode.ES0001,
                        unknownQuery.FirstOrDefault()));
                }
            }
        }


        #endregion
    }
}
